--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tests/backend/test_notifications.py ee9ed05338feee2e71aa3c839fd716c9a6bc2315 (ee9ed053) Text, 12.46 KB
Tff7b72import T7ee787os
Tff7b72import T7ee787time
Tff7b72from T7ee787contextlib Tff7b72import Te6edf3ExitStack
Tff7b72from T7ee787unittestT7ee787.T7ee787mock Tff7b72import Te6edf3MagicMockTb4b4b4, Te6edf3patch
Tff7b72import T7ee787pytest
Tff7b72import T7ee787RNS
Tff7b72from T7ee787hypothesis Tff7b72import Te6edf3HealthCheckTb4b4b4, Te6edf3givenTb4b4b4, Te6edf3settings
Tff7b72from T7ee787hypothesis Tff7b72import Te6edf3strategies Tff7b72as Te6edf3st
Tff7b72from T7ee787meshchatxT7ee787.T7ee787meshchat Tff7b72import Te6edf3ReticulumMeshChat
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787database Tff7b72import Te6edf3Database
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787databaseT7ee787.T7ee787provider Tff7b72import Te6edf3DatabaseProvider
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787databaseT7ee787.T7ee787schema Tff7b72import Te6edf3DatabaseSchema
Tf0883e@pytestTff7b72.Td2a8fffixture
Tff7b72def Td2a8fftemp_dbTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
Te6edf3db_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3tmp_pathTb4b4b4, Ta5d6ff"Ta5d6fftest_notifications.dbTa5d6ff"Tb4b4b4)
Tff7b72yield Te6edf3db_path
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3db_pathTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3db_pathTb4b4b4)
Tf0883e@pytestTff7b72.Td2a8fffixture
Tff7b72def Td2a8ffdbTb4b4b4(Te6edf3temp_dbTb4b4b4)Tb4b4b4:
Te6edf3provider Tff7b72= Te6edf3DatabaseProviderTb4b4b4(Te6edf3temp_dbTb4b4b4)
Te6edf3schema Tff7b72= Te6edf3DatabaseSchemaTb4b4b4(Te6edf3providerTb4b4b4)
Te6edf3schemaTff7b72.Td2a8ffinitializeTb4b4b4(Tb4b4b4)
Te6edf3database Tff7b72= Te6edf3DatabaseTb4b4b4(Te6edf3temp_dbTb4b4b4)
Tff7b72yield Te6edf3database
Te6edf3databaseTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Te6edf3providerTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Tf0883e@pytestTff7b72.Td2a8fffixture
Tff7b72def Td2a8ffmock_appTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
T8b949e# Save real Identity class to use as base for our mock class
Te6edf3real_identity_class Tff7b72= Te6edf3RNSTff7b72.Td2a8ffIdentity
Tff7b72class T56d364MockIdentityClassTb4b4b4(Te6edf3real_identity_classTb4b4b4)Tb4b4b4:
Tff7b72def Tff7b72__init__Tb4b4b4(Tff7b72selfTb4b4b4, Tff7b72*Te6edf3argsTb4b4b4, Tff7b72*Tff7b72*Te6edf3kwargsTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffhash Tff7b72= Ta5d6ffbTa5d6ff"Ta5d6fftest_hash_32_bytes_long_01234567Ta5d6ff"
Tff7b72selfTff7b72.Td2a8ffhexhash Tff7b72= Tff7b72selfTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
Tff7b72with Te6edf3ExitStackTb4b4b4(Tb4b4b4) Tff7b72as Te6edf3stackTb4b4b4:
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffRNS.IdentityTa5d6ff"Tb4b4b4, Te6edf3MockIdentityClassTb4b4b4)Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffRNS.ReticulumTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffRNS.TransportTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffLXMF.LXMRouterTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.TelephoneManagerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.VoicemailManagerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.RingtoneManagerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.RNCPHandlerTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.RNStatusHandlerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.RNProbeHandlerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.TranslatorHandlerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.ArchiverManagerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.MapManagerTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.MessageHandlerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffmeshchatx.src.backend.identity_context.AnnounceManagerTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(Te6edf3patchTb4b4b4(Ta5d6ff"Ta5d6ffthreading.ThreadTa5d6ff"Tb4b4b4)Tb4b4b4)
Te6edf3mock_id Tff7b72= Te6edf3MockIdentityClassTb4b4b4(Tb4b4b4)
Te6edf3mock_idTff7b72.Td2a8ffget_private_key Tff7b72= Te6edf3MagicMockTb4b4b4(Te6edf3return_valueTff7b72=Ta5d6ffbTa5d6ff"Ta5d6fftest_private_keyTa5d6ff"Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(Te6edf3MockIdentityClassTb4b4b4, Ta5d6ff"Ta5d6fffrom_fileTa5d6ff"Tb4b4b4, Te6edf3return_valueTff7b72=Te6edf3mock_idTb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(Te6edf3MockIdentityClassTb4b4b4, Ta5d6ff"Ta5d6ffrecallTa5d6ff"Tb4b4b4, Te6edf3return_valueTff7b72=Te6edf3mock_idTb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(Te6edf3MockIdentityClassTb4b4b4, Ta5d6ff"Ta5d6fffrom_bytesTa5d6ff"Tb4b4b4, Te6edf3return_valueTff7b72=Te6edf3mock_idTb4b4b4)Tb4b4b4,
Tb4b4b4)
T8b949e# Patch background threads and other heavy init
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(
Te6edf3ReticulumMeshChatTb4b4b4,
Ta5d6ff"Ta5d6ffannounce_loopTa5d6ff"Tb4b4b4,
Te6edf3newTff7b72=Te6edf3MagicMockTb4b4b4(Te6edf3return_valueTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(
Te6edf3ReticulumMeshChatTb4b4b4,
Ta5d6ff"Ta5d6ffannounce_sync_propagation_nodesTa5d6ff"Tb4b4b4,
Te6edf3newTff7b72=Te6edf3MagicMockTb4b4b4(Te6edf3return_valueTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(
Te6edf3ReticulumMeshChatTb4b4b4,
Ta5d6ff"Ta5d6ffcrawler_loopTa5d6ff"Tb4b4b4,
Te6edf3newTff7b72=Te6edf3MagicMockTb4b4b4(Te6edf3return_valueTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(
Te6edf3ReticulumMeshChatTb4b4b4,
Ta5d6ff"Ta5d6ffauto_backup_loopTa5d6ff"Tb4b4b4,
Te6edf3newTff7b72=Te6edf3MagicMockTb4b4b4(Te6edf3return_valueTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
T8b949e# Prevent JSON serialization issues with MagicMocks
Te6edf3stackTff7b72.Td2a8ffenter_contextTb4b4b4(
Te6edf3patchTff7b72.Td2a8ffobjectTb4b4b4(
Te6edf3ReticulumMeshChatTb4b4b4,
Ta5d6ff"Ta5d6ffsend_config_to_websocket_clientsTa5d6ff"Tb4b4b4,
Te6edf3return_valueTff7b72=Tff7b72NoneTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3app Tff7b72= Te6edf3ReticulumMeshChatTb4b4b4(
Te6edf3identityTff7b72=Te6edf3mock_idTb4b4b4,
Te6edf3storage_dirTff7b72=Tffa657strTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4,
Te6edf3reticulum_config_dirTff7b72=Tffa657strTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4,
Tb4b4b4)
T8b949e# Use our real test database
Te6edf3appTff7b72.Td2a8ffdatabase Tff7b72= Te6edf3db
Te6edf3appTff7b72.Td2a8ffwebsocket_broadcast Tff7b72= Te6edf3MagicMockTb4b4b4(Te6edf3side_effectTff7b72=Tff7b72lambda Te6edf3dataTb4b4b4: Tff7b72NoneTb4b4b4)
Tff7b72yield Te6edf3app
Te6edf3appTff7b72.Td2a8ffteardown_identityTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8fftest_add_get_notificationsTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
T8b949e"""Test basic notification storage and retrieval."""
Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffadd_notificationTb4b4b4(
Te6edf3notification_typeTff7b72=Ta5d6ff"Ta5d6fftest_typeTa5d6ff"Tb4b4b4,
Te6edf3remote_hashTff7b72=Ta5d6ff"Ta5d6fftest_hashTa5d6ff"Tb4b4b4,
Te6edf3titleTff7b72=Ta5d6ff"Ta5d6ffTest TitleTa5d6ff"Tb4b4b4,
Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffTest ContentTa5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Tb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6fftest_typeTa5d6ff"
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffremote_hashTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6fftest_hashTa5d6ff"
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffTest TitleTa5d6ff"
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffTest ContentTa5d6ff"
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffis_viewedTa5d6ff"Tb4b4b4] Tff7b72== T79c0ff0
Tff7b72def Td2a8fftest_mark_notifications_as_viewedTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
T8b949e"""Test marking notifications as viewed."""
Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffadd_notificationTb4b4b4(Ta5d6ff"Ta5d6fftype1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhash1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fftitle1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffcontent1Ta5d6ff"Tb4b4b4)
Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffadd_notificationTb4b4b4(Ta5d6ff"Ta5d6fftype2Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhash2Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fftitle2Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffcontent2Ta5d6ff"Tb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Tb4b4b4)
Te6edf3n_ids Tff7b72= Tb4b4b4[Te6edf3nTb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4] Tff7b72for Te6edf3n Tff7b72in Te6edf3notificationsTb4b4b4]
Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffmark_notifications_as_viewedTb4b4b4(Tb4b4b4[Te6edf3n_idsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4]Tb4b4b4)
Te6edf3unread Tff7b72= Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Te6edf3filter_unreadTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3unreadTb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Te6edf3unreadTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffidTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3n_idsTb4b4b4[T79c0ff1Tb4b4b4]
Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffmark_notifications_as_viewedTb4b4b4(Tb4b4b4) T8b949e# Mark all
Te6edf3unread_all Tff7b72= Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Te6edf3filter_unreadTff7b72=Tff7b72TrueTb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3unread_allTb4b4b4) Tff7b72== T79c0ff0
Tff7b72def Td2a8fftest_missed_call_notificationTb4b4b4(Te6edf3mock_appTb4b4b4)Tb4b4b4:
T8b949e"""Test that a missed call triggers a notification."""
Te6edf3caller_identity Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3caller_identityTff7b72.Td2a8ffhash Tff7b72= Ta5d6ffbTa5d6ff"Ta5d6ffcaller_hash_32_bytes_long_012345Ta5d6ff"
Te6edf3caller_hash Tff7b72= Te6edf3caller_identityTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)
T8b949e# Mock telephone manager state for missed call
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_is_incoming Tff7b72= Tff7b72True
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_status_at_end Tff7b72= T79c0ff4 T8b949e# Ringing
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_start_time Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4) Tff7b72- T79c0ff10
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_was_established Tff7b72= Tff7b72False
Te6edf3mock_appTff7b72.Td2a8ffon_telephone_call_endedTb4b4b4(Te6edf3caller_identityTb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Tb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6fftelephone_missed_callTa5d6ff"
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffremote_hashTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3caller_hash
T8b949e# Verify websocket broadcast
Tff7b72assert Te6edf3mock_appTff7b72.Td2a8ffwebsocket_broadcastTff7b72.Td2a8ffcalled
Tff7b72def Td2a8fftest_voicemail_notificationTb4b4b4(Te6edf3mock_appTb4b4b4)Tb4b4b4:
T8b949e"""Test that a new voicemail triggers a notification."""
Te6edf3remote_hash Tff7b72= Ta5d6ff"Ta5d6ffremote_hash_hexTa5d6ff"
Te6edf3remote_name Tff7b72= Ta5d6ff"Ta5d6ffRemote UserTa5d6ff"
Te6edf3duration Tff7b72= T79c0ff15
Te6edf3mock_appTff7b72.Td2a8ffon_new_voicemail_receivedTb4b4b4(Te6edf3remote_hashTb4b4b4, Te6edf3remote_nameTb4b4b4, Te6edf3durationTb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Tb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6fftelephone_voicemailTa5d6ff"
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffremote_hashTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3remote_hash
Tff7b72assert Ta5d6ff"Ta5d6ff15sTa5d6ff" Tff7b72in Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4]
T8b949e# Verify websocket broadcast
Tff7b72assert Te6edf3mock_appTff7b72.Td2a8ffwebsocket_broadcastTff7b72.Td2a8ffcalled
Tf0883e@settingsTb4b4b4(Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4, Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fffunction_scoped_fixtureTb4b4b4]Tb4b4b4)
Tf0883e@givenTb4b4b4(
Te6edf3notification_typeTff7b72=Te6edf3stTff7b72.Td2a8fftextTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff50Tb4b4b4)Tb4b4b4,
Te6edf3remote_hashTff7b72=Te6edf3stTff7b72.Td2a8fftextTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff64Tb4b4b4)Tb4b4b4,
Te6edf3titleTff7b72=Te6edf3stTff7b72.Td2a8fftextTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff100Tb4b4b4)Tb4b4b4,
Te6edf3contentTff7b72=Te6edf3stTff7b72.Td2a8fftextTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff500Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_notification_fuzzingTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3notification_typeTb4b4b4, Te6edf3remote_hashTb4b4b4, Te6edf3titleTb4b4b4, Te6edf3contentTb4b4b4)Tb4b4b4:
T8b949e"""Fuzz notification storage with varied data."""
Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffadd_notificationTb4b4b4(Te6edf3notification_typeTb4b4b4, Te6edf3remote_hashTb4b4b4, Te6edf3titleTb4b4b4, Te6edf3contentTb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Te6edf3limitTff7b72=T79c0ff1Tb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff1
T8b949e# We don't assert content match exactly if there are encoding issues,
T8b949e# but sqlite should handle most strings.
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72== Te6edf3notification_type
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_notifications_apiTb4b4b4(Te6edf3mock_appTb4b4b4)Tb4b4b4:
T8b949e"""Test the notifications API endpoint."""
T8b949e# Add some notifications
Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffadd_notificationTb4b4b4(Ta5d6ff"Ta5d6fftype1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhash1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fftitle1Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffcontent1Ta5d6ff"Tb4b4b4)
T8b949e# Mock request
Te6edf3request Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3requestTff7b72.Td2a8ffquery Tff7b72= Tb4b4b4{Ta5d6ff"Ta5d6ffunreadTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fffalseTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fflimitTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff10Ta5d6ff"Tb4b4b4}
T8b949e# We need to mock local_lxmf_destination as it's used in notifications_get
Te6edf3mock_appTff7b72.Td2a8fflocal_lxmf_destination Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3mock_appTff7b72.Td2a8fflocal_lxmf_destinationTff7b72.Td2a8ffhexhash Tff7b72= Ta5d6ff"Ta5d6fflocal_hashTa5d6ff"
T8b949e# Also mock message_handler.get_conversations
Te6edf3mock_appTff7b72.Td2a8ffmessage_handlerTff7b72.Td2a8ffget_conversationsTff7b72.Td2a8ffreturn_value Tff7b72= Tb4b4b4[Tb4b4b4]
T8b949e# Find the route handler
T8b949e# Since it's defined inside ReticulumMeshChat.run, we might need to find it
T8b949e# or just call the method if we can.
T8b949e# Actually, let's just test the logic by calling the handler directly if we can find it.
T8b949e# But it's defined as a nested function.
T8b949e# Alternatively, we can test the DAOs and meshchat.py logic that the handler uses.
T8b949e# For now, let's verify that system notifications are correctly combined with LXMF messages.
T8b949e# This is done in notifications_get.
T8b949e# Let's test a spike of notifications
Tff7b72for Te6edf3i Tff7b72in Tffa657rangeTb4b4b4(T79c0ff100Tb4b4b4)Tb4b4b4:
Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffadd_notificationTb4b4b4(
Te6edf3notification_typeTff7b72=Ta5d6fffTa5d6ff"Ta5d6fftypeTffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4,
Te6edf3remote_hashTff7b72=Ta5d6fffTa5d6ff"Ta5d6ffhashTffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4,
Te6edf3titleTff7b72=Ta5d6fffTa5d6ff"Ta5d6fftitleTffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4,
Te6edf3contentTff7b72=Ta5d6fffTa5d6ff"Ta5d6ffcontentTffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Te6edf3limitTff7b72=T79c0ff50Tb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff50
Tf0883e@settingsTb4b4b4(Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4, Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fffunction_scoped_fixtureTb4b4b4]Tb4b4b4)
Tf0883e@givenTb4b4b4(
Te6edf3remote_hashTff7b72=Te6edf3stTff7b72.Td2a8fftextTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff64Tb4b4b4)Tb4b4b4,
Te6edf3remote_nameTff7b72=Te6edf3stTff7b72.Td2a8ffone_ofTb4b4b4(Te6edf3stTff7b72.Td2a8ffnoneTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3stTff7b72.Td2a8fftextTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff100Tb4b4b4)Tb4b4b4)Tb4b4b4,
Te6edf3durationTff7b72=Te6edf3stTff7b72.Td2a8ffintegersTb4b4b4(Te6edf3min_valueTff7b72=T79c0ff0Tb4b4b4, Te6edf3max_valueTff7b72=T79c0ff3600Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_voicemail_notification_fuzzingTb4b4b4(Te6edf3mock_appTb4b4b4, Te6edf3remote_hashTb4b4b4, Te6edf3remote_nameTb4b4b4, Te6edf3durationTb4b4b4)Tb4b4b4:
T8b949e"""Fuzz voicemail notification triggering."""
Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffDELETE FROM notificationsTa5d6ff"Tb4b4b4)
Te6edf3mock_appTff7b72.Td2a8ffon_new_voicemail_receivedTb4b4b4(Te6edf3remote_hashTb4b4b4, Te6edf3remote_nameTb4b4b4, Te6edf3durationTb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Tb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6fftelephone_voicemailTa5d6ff"
Tff7b72assert Te6edf3remote_hash Tff7b72in Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4] Tff7b72or Tb4b4b4(
Te6edf3remote_name Tff7b72and Te6edf3remote_name Tff7b72in Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4]
Tb4b4b4)
Tf0883e@settingsTb4b4b4(Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4, Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fffunction_scoped_fixtureTb4b4b4]Tb4b4b4)
Tf0883e@givenTb4b4b4(
Te6edf3remote_hashTff7b72=Te6edf3stTff7b72.Td2a8fftextTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff32Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff64Tb4b4b4)Tb4b4b4, T8b949e# Hex hash
Te6edf3status_codeTff7b72=Te6edf3stTff7b72.Td2a8ffintegersTb4b4b4(Te6edf3min_valueTff7b72=T79c0ff0Tb4b4b4, Te6edf3max_valueTff7b72=T79c0ff10Tb4b4b4)Tb4b4b4,
Te6edf3call_was_establishedTff7b72=Te6edf3stTff7b72.Td2a8ffbooleansTb4b4b4(Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8fftest_missed_call_notification_fuzzingTb4b4b4(
Te6edf3mock_appTb4b4b4,
Te6edf3remote_hashTb4b4b4,
Te6edf3status_codeTb4b4b4,
Te6edf3call_was_establishedTb4b4b4,
Tb4b4b4)Tb4b4b4:
T8b949e"""Fuzz missed call notification triggering."""
Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffproviderTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffDELETE FROM notificationsTa5d6ff"Tb4b4b4)
Te6edf3caller_identity Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3caller_identityTff7b72.Td2a8ffhash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Te6edf3remote_hashTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Te6edf3caller_identityTff7b72.Td2a8ffhash Tff7b72= Te6edf3remote_hashTff7b72.Td2a8ffencodeTb4b4b4(Tb4b4b4)Tb4b4b4[Tb4b4b4:T79c0ff32Tb4b4b4]
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_is_incoming Tff7b72= Tff7b72True
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_status_at_end Tff7b72= Te6edf3status_code
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_start_time Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3mock_appTff7b72.Td2a8fftelephone_managerTff7b72.Td2a8ffcall_was_established Tff7b72= Te6edf3call_was_established
Te6edf3mock_appTff7b72.Td2a8ffon_telephone_call_endedTb4b4b4(Te6edf3caller_identityTb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3mock_appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Tb4b4b4)
T8b949e# Notification is created if incoming and not established, regardless of status_code
Tff7b72if Tff7b72not Te6edf3call_was_establishedTb4b4b4:
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Te6edf3notificationsTb4b4b4[T79c0ff0Tb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6fftelephone_missed_callTa5d6ff"
Tff7b72elseTb4b4b4:
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== T79c0ff0
Tf0883e@settingsTb4b4b4(Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4, Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fffunction_scoped_fixtureTb4b4b4]Tb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3num_notifsTff7b72=Te6edf3stTff7b72.Td2a8ffintegersTb4b4b4(Te6edf3min_valueTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_valueTff7b72=T79c0ff200Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_notification_spike_fuzzingTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3num_notifsTb4b4b4)Tb4b4b4:
T8b949e"""Test handling a spike of notifications."""
Tff7b72for Te6edf3i Tff7b72in Tffa657rangeTb4b4b4(Te6edf3num_notifsTb4b4b4)Tb4b4b4:
Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffadd_notificationTb4b4b4(Ta5d6fffTa5d6ff"Ta5d6fftypeTffd700{Te6edf3iTffd700}Ta5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4)
Te6edf3notifications Tff7b72= Te6edf3dbTff7b72.Td2a8ffmiscTff7b72.Td2a8ffget_notificationsTb4b4b4(Te6edf3limitTff7b72=Te6edf3num_notifsTb4b4b4)
Tff7b72assert Tffa657lenTb4b4b4(Te6edf3notificationsTb4b4b4) Tff7b72== Te6edf3num_notifs
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────